/**
 * Copyright (c) 2020 kedacom
 * OpenATC is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 * http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 **/
package com.openatc.agent.realm;

import com.google.gson.JsonObject;
import com.openatc.agent.utils.TokenUtil;
import com.openatc.core.common.Constants;
import com.openatc.core.common.IErrorEnumImplOuter;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authc.AuthenticatingFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import static com.openatc.core.common.Constants.REQUEST_HEADER_TOKEN_KEY;
import static com.openatc.core.common.Constants.REQUEST_PARAM_TOKEN_KEY;
import static com.openatc.core.common.IErrorEnumImplOuter.*;

public class JwtAuthenticationFilter extends AuthenticatingFilter {

    private Logger logger = LoggerFactory.getLogger(JwtAuthenticationFilter.class);
    private static final Map<String,Integer> urlmap = new HashMap<>();

    @Override
    protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) {
        HttpServletRequest httpRequest = (HttpServletRequest) request;

//        WebUtils.toHttp(response);
        // 先从Header里面获取
        String token = httpRequest.getHeader(REQUEST_HEADER_TOKEN_KEY);
        String remoteIP = getRemortIP(httpRequest);
        String url = httpRequest.getRequestURI();

        createUriMap(remoteIP,url);

        if (token == null || token.length() == 0) {
            // 获取不到再从Parameter中拿
            token = httpRequest.getParameter(REQUEST_PARAM_TOKEN_KEY);
            // 还是获取不到再从Cookie中拿
            if (token == null || token.length() == 0) {
                return JwtToken.builder().ip(remoteIP).url(url).build();
            }
        }

//        boolean flag = tokenUtil.validateToken(token);
//        if(!flag){
//            //从token中拿用户名
//            String username = tokenUtil.getUsernameFromToken(token);
//            httpResponse.setHeader("token",tokenUtil.generateToken(username, System.currentTimeMillis()));
//        }
        return JwtToken.builder()
                .token(token)
                .principal(token)
                .ip(remoteIP)
                .url(url)
                .build();
    }

    private void createUriMap(String ip, String requestURI) {
        String url = ip + ":" + requestURI;
        // url访问计数
        Integer count = urlmap.get(url);
        if (count == null)
            count = 1;
        else
            count+=1;

        urlmap.put(url,count);

        // 打印频繁请求的接口
        if(count % 1000 == 0)
            logger.warn("Url :" + requestURI + " count: " + count );
    }

    public String getRemortIP(HttpServletRequest request) {
        if (request.getHeader("x-forwarded-for") == null) {
            return request.getRemoteAddr();
        }
        return request.getHeader("x-forwarded-for");
    }

    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
        boolean login = false;

        try{
            login = executeLogin(request, response);
        }catch (Exception e){
            HttpServletRequest httpRequest = (HttpServletRequest) request;
            logger.warn("isAccessAllowed url:{}, error:{}",httpRequest.getRequestURI(), e.getMessage());
        }

        // 跨域设置
//        HttpServletRequest httpRequest = (HttpServletRequest) request;
//        HttpServletResponse servletResponse = (HttpServletResponse) response;
//        servletResponse.setCharacterEncoding("UTF-8");
//        servletResponse.setContentType("application/json;charset=UTF-8");
//        servletResponse.setHeader("Access-Control-Allow-Origin", httpRequest.getHeader("Origin"));
//        servletResponse.setHeader("Access-Control-Allow-Methods", "*");
//        servletResponse.setHeader("Access-Control-Allow-Headers", "*");

        return login;
    }

    @Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) {
        return false;
    }

    @Override
    protected boolean onLoginSuccess(AuthenticationToken token, Subject subject, ServletRequest request,
                                     ServletResponse response) {
        return true;
    }

    @Override
    protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException ae, ServletRequest request,
                                     ServletResponse response) {

        //读取本地文件， 若token和本地文件的token也相同，则放行
//        String checktoken = ((JwtToken) token).getToken();
//
//        if (AgentApplication.tokenlist != null) {
//            for (String s : AgentApplication.tokenlist) {
//                if (s.equals(checktoken))
//                    return true;
//            }
//        }
//        //调试模式直接返回true
//        if (!AgentApplication.shiroOpen) return true;

        response.setContentType("application/json");
        JsonObject jsonObject;

        if (ae.getMessage().equals(E_3013.getErrorMsg())) {
            jsonObject = errorToJson(E_3013);
        } else if (ae.getMessage().equals(E_3014.getErrorMsg())) {
            jsonObject = errorToJson(E_3014);
        } else if (ae.getMessage().equals(E_3018.getErrorMsg())) {
            jsonObject = errorToJson(E_3018);
        } else if (ae.getMessage().equals(E_3024.getErrorMsg())) {
            String username = TokenUtil.getCurrentUserName();
            jsonObject = errorToJson(E_3024,username);
        } else if (ae.getMessage().equals(E_3025.getErrorMsg())){
            jsonObject = errorToJson(E_3025);
        }
        else {
            jsonObject = errorToJson(E_3015);
        }

        try {
            response.getWriter().write(jsonObject.toString());
        } catch (IOException e) {
            logger.warn("onLoginFailure error:{}", e.getMessage());
        }
        return false;
    }

    private JsonObject errorToJson(IErrorEnumImplOuter iErrorEnumImplOuter) {
        JsonObject errorJson = new JsonObject();
        errorJson.addProperty(Constants.CODE, iErrorEnumImplOuter.getErrorCode());
        errorJson.addProperty(Constants.MESSAGE, iErrorEnumImplOuter.getErrorMsg());
        errorJson.addProperty("success", false);
        return errorJson;
    }

    private JsonObject errorToJson(IErrorEnumImplOuter iErrorEnumImplOuter,String content) {
        JsonObject jsonObject = errorToJson(iErrorEnumImplOuter);
        JsonObject data = new JsonObject();
        data.addProperty("username",content);
        jsonObject.add("data",data);
        return jsonObject;
    }
}
